這次主要會實作 ImageBitmap 以及 OffscreenCanvas 兩個新的 API,這兩個目前支持度最好的目前只有最新版的 chrome,所以目前在使用上請記得相容性問題。那就繼續往下吧!
還記得上章說過在主線程跟 worker 中傳遞的資料會在兩邊都被複製一份,所以在資料大時會造成額外的開銷,除了有實作 Transferable 介面的ArrayBuffer、MessagePort、ImageBitmap、OffscreenCanvas 這幾種類型,傳遞上可以想像成物件類型的傳遞,是使用址來做為傳遞,所以不會在進行複製。
所以我們現在要做的事情就是將從 video 取回來的影像轉為 ImageBitmap 傳遞給 worker,並且將濾鏡在裡面計算完畢。我們先在 play 成功之後進行下列動作,
captureStream ,這個將會回傳一個 MediaStream 物件,可以讓我們獲得即時的資料getVideoTracks 來獲得目前的影像 ( getVideoTracks 會回傳目前所有軌道的影像,因為我們目前只有一軌,所以直接拿第一個, MediaStream 也可以增加軌道或者音頻,如果要做編輯或混音的時候可能會用到 )video 給 ImageCapture 使用, ImageCapture 提供了一些方法可以將目前的影片保存下來,將下來我們會使用其中一個。 video
.play()
.then(() => {
console.log('play video start')
const stream = video.captureStream()
const track = stream.getVideoTracks()[0]
this.capture = new ImageCapture(track)
this.drawCanvas()
})
做完上面的初始化之後,接著在原本更新畫面的方法裡面做一些改變,主要就是使用 grabFrame,這個將會回傳一個 ImageBitmap,所以我們就可以使用 postMessage 傳遞,要注意的是第二個參數我們使用了 [imageBitmap] ,有實作 Transferable 的物件類型,可以通過此方法,將資料不需透過複製就傳給 worker,但要注意的是在傳遞過後,主線程就喪失了這個資料的使用權。
this.capture
.grabFrame()
.then(imageBitmap => {
worker.postMessage(
{
imageBitmap,
sliderValue: this.sliderValue,
type: 'process'
},
[imageBitmap]
)
})
.catch(err => {
// 目前發現不會每次都成功,似乎跟原本影片 fps 沒有達到瀏覽器更新頻率有關
// console.log('play video error', err)
})
接著在 worker 裡接收
let imageBitmapTmp
let sliderValueTmp
onmessage = function(e) {
if (e.data.type === 'process' && context) {
const { imageBitmap, sliderValue } = e.data
imageBitmapTmp = imageBitmap
sliderValueTmp = sliderValue
}
}
到這裡我們就成功將圖像送給 worker ,接下來遇到的問題就是我們平時是使用 Canvas 去獲得最後要修改的 ImageData ,但是在 worker 裡面卻無法獲得 DOM,明天來看要如何解決吧!

拉達克的首都最熱鬧的一條街,雖然是首都但也是電力不穩,不過已經是這趟旅程裡面生活機能最好的地方了
今天提到了 ImageBitmap ,其實他的創造來源可以有很多種,圖片、SVG等都可以當作來源。在使用上 MDN 表示使用在 Cavas、 WebGL 上會更有效率。而且在 worker 中也可以使用,所以如果以後有圖片需要載入的動作就可以透過 worker 進行。那就明天見囉!